package cn.jw.starter.common.scheduletask;

import java.util.HashMap;
import java.util.Map;

import com.alibaba.fastjson2.JSON;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.jw.starter.common.scheduletask.model.ScheduledTaskInfo;
import lombok.extern.slf4j.Slf4j;

/**
 * 基于Spring实现的定时任务管理，支持以下特性： 1.动态更新cron表达式，而不用重启服务
 */
@Slf4j
public class JwScheduledTaskService {
    private final Map<String, ScheduledTaskInfo> taskClass2TaskInfo = new HashMap<>();

    /**
     * 将所有定时任务添加到Spring框架
     */
    public void startAllTask() {
        ScheduledTaskCore scheduledTaskCore = SpringUtil.getBean(ScheduledTaskCore.class);
        final Map<String, ScheduledTaskInfo> taskInfoList = getAllTask();
        for (Map.Entry<String, ScheduledTaskInfo> entry : taskInfoList.entrySet()) {
            final ScheduledTaskInfo taskInfo = entry.getValue();
            scheduledTaskCore.injectTaskToSpring(taskInfo);
        }
    }

    private Map<String, ScheduledTaskInfo> getAllTask() {
        return new HashMap<>(taskClass2TaskInfo);
    }

    /**
     * 添加定时任务到Spring定时任务框架，在ready之前的init或start阶段调用
     *
     * @param cronTriggerExpression
     *            cron表达式
     * @param runnableClass
     *            runnable任务对应的class
     */
    public <T extends Runnable> void addTask(String cronTriggerExpression, Class<T> runnableClass, String taskName) {
        if (StrUtil.isEmpty(cronTriggerExpression)) {
            throw new IllegalArgumentException("%s定时任务的cronTriggerExpression不能为空");
        }

        final String key = runnableClass.getName();
        final ScheduledTaskInfo taskInfo = taskClass2TaskInfo.get(key);
        if (taskInfo != null) {
            throw new IllegalArgumentException("%s定时任务重复添加");
        }

        taskClass2TaskInfo.put(key, new ScheduledTaskInfo(cronTriggerExpression, runnableClass, taskName));
        log.info("SCHEDULED_TASK-添加定时任务成功, runnable = {}, taskName = {}, cronTriggerExpression = {}", key, taskName,
            cronTriggerExpression);
    }

    /**
     * 刷新cron表达式，在配置文件刷新后调用
     *
     * @param cronTriggerExpression
     *            cron表达式
     * @param runnableClass
     *            runnable任务对应的class
     */
    public <T extends Runnable> void updateTask(String cronTriggerExpression, Class<T> runnableClass) {
        if (StrUtil.isEmpty(cronTriggerExpression)) {
            throw new IllegalArgumentException("定时任务的cronTriggerExpression不能为空");
        }

        final String key = runnableClass.getName();
        final ScheduledTaskInfo taskInfo = taskClass2TaskInfo.get(key);
        if (taskInfo == null) {
            log.error(
                "SCHEDULED_TASK-刷新定时任务调度表达式失败，无法找到定时任务，runnable = {}, cronTriggerExpression = {}s, taskClass2TaskInfo = {}",
                runnableClass.getName(), cronTriggerExpression, JSON.toJSONString(taskClass2TaskInfo));
            return;
        }

        if (taskInfo.getCronTriggerExpression().equals(cronTriggerExpression)) {
            log.info("SCHEDULED_TASK-刷新定时任务调度表达式，未变更, runnable = %s, taskName = {}, cronTriggerExpression = {}", key,
                taskInfo.getTaskName(), cronTriggerExpression);
            return;
        }

        taskInfo.setCronTriggerExpression(cronTriggerExpression);
        final ScheduledTaskCore instance = SpringUtil.getBean(ScheduledTaskCore.class);
        if (instance != null) {
            instance.updateScheduledTask(taskInfo);
        }

        log.info("SCHEDULED_TASK-刷新定时任务调度表达式成功, runnable = {}, taskName = {}, cronTriggerExpression = {}", key,
            taskInfo.getTaskName(), cronTriggerExpression);
    }

}
